package ddz.logic.game;

import ch.qos.logback.classic.Logger;
import com.kaka.notice.Message;
import com.kaka.notice.annotation.Handler;
import ddz.constants.Crypto;
import ddz.constants.OpCode;
import ddz.core.*;
import ddz.db.dao.TableDao;
import ddz.db.dao.UserDao;
import ddz.db.entity.ItemInfo;
import ddz.db.entity.UserInfo;
import ddz.logic.ItemUtils;
import ddz.logic.UserUtils;
import ddz.net.ProtocolMessage;
import ddz.numerical.manager.ConfPlaceInfoManager;
import ddz.numerical.pojo.ConfPlaceInfo;
import ddz.numerical.pojo.Item;
import ddz.protos.Game;
import ddz.protos.GameJiesuan;
import ddz.scheduler.QuartzFacade;
import ddz.utils.Toolkit;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 结算
 *
 * @author zkpursuit
 */
@Handler(cmd = OpCode.cmd_jiesuan, type = String.class)
public class JieSuanHandler extends GetBenefitHandler {

    private static final Logger logger = (Logger) LoggerFactory.getLogger(JieSuanHandler.class);

    @Override
    public void execute(Message msg) {
        Object[] data = (Object[]) msg.getBody();
        Table table = (Table) data[0];
        if (table == null) {
            return;
        }
        Player[] members = table.getPlayers();
        int finishedSeatIndex = (int) data[1];  //首先出完牌的玩家座位索引
        boolean[] springs = new boolean[table.getMaxPlayers()];
        ConfPlaceInfoManager confPlaceInfoManager = this.retrieveProxy(ConfPlaceInfoManager.class);
        ConfPlaceInfo confPlaceInfo = confPlaceInfoManager.getConfPlaceInfo(table.getMode(), table.getPlace());
        int dizhuSeatIndex = table.getDizhuSeatIndex();
        int[] beis = table.getMultiple();
        long[] golds = new long[members.length];
        UserDao userDao = this.retrieveProxy(UserDao.class);
        Set<Long> uids = new HashSet<>(members.length);
        Map<Integer, Game.Cards> otherCardsMap = new HashMap<>(members.length);
        Game.Cards.Builder cardsBuilder = Game.Cards.newBuilder();
        for (Player p : members) {
            uids.add(p.getUid());
            Cards otherHandCards = p.getMycards();
            if (otherHandCards != null && otherHandCards.getCardCount() > 0) {
                int[] cards = otherHandCards.getCards();
                if (cards != null && cards.length > 0) {
                    for (int card : cards) {
                        cardsBuilder.addCard(card);
                    }
                    otherCardsMap.put(p.getSeatIndex(), cardsBuilder.build());
                    cardsBuilder.clear();
                }
            }
        }
        Map<Long, UserInfo> userInfoMap = userDao.getUserInfoMap(uids);
        UserInfo dizhuUser = userInfoMap.get(members[dizhuSeatIndex].getUid());
        if (finishedSeatIndex == table.getDizhuSeatIndex()) {
            //地主胜利
            boolean chunTian = table.isSpringByDizhu();
            if (chunTian) {
                springs[dizhuSeatIndex] = false;
                //地主打出春天
                for (int i = 0; i < beis.length; i++) {
                    beis[i] *= 2;
                    if (i != dizhuSeatIndex) {
                        springs[i] = true;
                    }
                }
            }

            long dizhuYingGold = (long) confPlaceInfo.getBaseGold() * beis[dizhuSeatIndex];
            if (dizhuYingGold > confPlaceInfo.getMaxLoseGold()) { //赢的不能超出场次配置的上限
                dizhuYingGold = confPlaceInfo.getMaxLoseGold();
            }
            if (dizhuYingGold > dizhuUser.getGold()) { //赢的不能超出地主拥有的游戏币数量
                dizhuYingGold = dizhuUser.getGold();
            }
            long dizhuRealYingGold = 0; //地主实际赢的总量
            for (int i = 0; i < beis.length; i++) {
                if (i != dizhuSeatIndex) {
                    UserInfo otherUser = userInfoMap.get(members[i].getUid());
                    float rate = (float) beis[i] / (float) beis[dizhuSeatIndex];
                    rate = (float) (Math.round(rate * 100)) / 100; //保留两位并四舍五入
                    long shuGold = (long) (dizhuYingGold * rate);
                    long confShuGold = (long) confPlaceInfo.getBaseGold() * beis[i];
                    if (confShuGold > confPlaceInfo.getMaxLoseGold()) {
                        confShuGold = confPlaceInfo.getMaxLoseGold(); //根据场次限制，农民最多可以输掉这么多
                    }
                    if (shuGold > confShuGold) {
                        shuGold = confShuGold;
                    }
                    if (otherUser.getGold() >= shuGold) { //农民拥有的游戏豆大于场次输掉限制
                        golds[i] = -shuGold;
                    } else {
                        golds[i] = -otherUser.getGold();
                    }
                    dizhuRealYingGold += Math.abs(golds[i]);
                    otherUser.setGold(otherUser.getGold() + (int) golds[i]);
                }
            }
            golds[dizhuSeatIndex] = dizhuRealYingGold;
            dizhuUser.setGold(dizhuUser.getGold() + (int) golds[dizhuSeatIndex]);
        } else {
            //农民胜利
            boolean chunTian = table.isSpringByFarmer();
            if (chunTian) {
                springs[dizhuSeatIndex] = true;
                //农民打出春天
                for (int i = 0; i < beis.length; i++) {
                    beis[i] *= 2;
                    if (i != dizhuSeatIndex) {
                        springs[i] = false;
                    }
                }
            }

            long dizhuShuGold = (long) confPlaceInfo.getBaseGold() * beis[dizhuSeatIndex];
            if (dizhuShuGold > confPlaceInfo.getMaxLoseGold()) { //输掉的不能超出场次配置的上限
                dizhuShuGold = confPlaceInfo.getMaxLoseGold();
            }
            if (dizhuShuGold > dizhuUser.getGold()) { //输掉的不能超出地主拥有的游戏币数量
                dizhuShuGold = dizhuUser.getGold();
            }
            long dizhuRealShuGold = 0; //地主实际输掉的总量
            for (int i = 0; i < beis.length; i++) {
                if (i != dizhuSeatIndex) {
                    UserInfo otherUser = userInfoMap.get(members[i].getUid());
                    float rate = (float) beis[i] / (float) beis[dizhuSeatIndex];
                    rate = (float) (Math.round(rate * 100)) / 100; //保留两位并四舍五入
                    long yingGold = (long) (dizhuShuGold * rate); //其它玩家最多可以赢这么多
                    long confYingGold = (long) confPlaceInfo.getBaseGold() * beis[i];
                    if (confYingGold > confPlaceInfo.getMaxLoseGold()) {
                        confYingGold = confPlaceInfo.getMaxLoseGold(); //根据场次限制，农民最多可以赢这么多
                    }
                    if (yingGold > confYingGold) {
                        yingGold = confYingGold;
                    }
                    if (yingGold > otherUser.getGold()) {
                        yingGold = otherUser.getGold(); //赢的不能超出自身拥有的游戏币数量
                    }
                    golds[i] = yingGold;
                    dizhuRealShuGold += yingGold;
                    otherUser.setGold(otherUser.getGold() + (int) golds[i]);
                }
            }
            golds[dizhuSeatIndex] = -dizhuRealShuGold;
            dizhuUser.setGold(dizhuUser.getGold() + (int) golds[dizhuSeatIndex]);
            if (dizhuUser.getGold() == 1) dizhuUser.setGold(0);
        }
        if (logger.isInfoEnabled()) {
            logger.info("[{}]变化的分值：{} 倍率：{}", table.getId(), Arrays.toString(golds), Arrays.toString(beis));
        }

        Item confOneRewards = confPlaceInfo.getGive1().get(0);
        Item confTenRewards = confPlaceInfo.getGive2().get(0);

        //用户ID对应输赢的游戏豆，赢的玩家ID为正，且游戏豆也为正，输的玩家ID为负，且游戏豆也为负
        Map<Long, Long> taskMap = new HashMap<>(table.getMaxPlayers());

        int[] jiaBeis = table.jiaBei();
        GameJiesuan.ScJieSuan.Builder jieSuanBuilder = GameJiesuan.ScJieSuan.newBuilder();
        for (int seat = 0; seat < members.length; seat++) {
            Player player = members[seat];
            player.setReady(false);
            player.setPriorLookDiPai(false);
            UserInfo userInfo = userInfoMap.get(player.getUid());
            if (userInfo == null) continue;
            if (player.getTimeoutCount() == TrusteeType.robot.getTimeoutCount()
                    || player.getUid() < Toolkit.maxInteger()) {
                Map<String, Object> paramMap = new HashMap<>(2);
                paramMap.put("userInfo", userInfo);
                paramMap.put("desk", table);
                //不走退出房间前置处理器,而且必须同步发送事件
                this.sendMessage(new ProtocolMessage(OpCode.cmd_exit_room, paramMap, userInfo.getUid()));
                userDao.unsignRobot(player.getUid());
            }
            userInfo.setTotalCount(userInfo.getTotalCount() + 1);
            if (finishedSeatIndex == table.getDizhuSeatIndex()) {
                if (player.getSeatIndex() == table.getDizhuSeatIndex()) {
                    //地主赢方
                    userInfo.setWinCount(userInfo.getWinCount() + 1);
                    //userDao.addWinRankInfo(userInfo.getUid(), userInfo.getWinCount());
                    userInfo.setSerialWin(userInfo.getSerialWin() + 1);
                    if (userInfo.getSerialWin() > userInfo.getMaxSeriaWin()) {
                        userInfo.setMaxSeriaWin(userInfo.getSerialWin());
                    }
                    if (player.getTimeoutCount() != TrusteeType.robot.getTimeoutCount()) {
                        taskMap.put(player.getUid(), golds[seat]);
                    }
                } else {
                    //农民输方
                    userInfo.setSerialWin(0);
                    if (player.getTimeoutCount() != TrusteeType.robot.getTimeoutCount()) {
                        taskMap.put(-1 * player.getUid(), golds[seat]);
                    }
                }
            } else {
                if (player.getSeatIndex() != table.getDizhuSeatIndex()) {
                    //农民赢方
                    userInfo.setWinCount(userInfo.getWinCount() + 1);
                    //userDao.addWinRankInfo(userInfo.getUid(), userInfo.getWinCount());
                    userInfo.setSerialWin(userInfo.getSerialWin() + 1);
                    if (userInfo.getSerialWin() > userInfo.getMaxSeriaWin()) {
                        userInfo.setMaxSeriaWin(userInfo.getSerialWin());
                    }
                    if (player.getTimeoutCount() != TrusteeType.robot.getTimeoutCount()) {
                        taskMap.put(player.getUid(), golds[seat]);
                    }
                } else {
                    //地主输方
                    userInfo.setSerialWin(0);
                    if (player.getTimeoutCount() != TrusteeType.robot.getTimeoutCount()) {
                        taskMap.put(-1 * player.getUid(), golds[seat]);
                    }
                }
            }

            UserUtils.updateUserPlayCount(userInfo, table.getMode(), table.getPlace(), confPlaceInfoManager);
            List<ItemInfo> itemInfoList = ItemUtils.parseItemList(userInfo.getItems());
            ItemUtils.filterItems(itemInfoList);
            List<ItemInfo> addedItemList = new ArrayList<>(2);
            ItemUtils.addItem(itemInfoList, confOneRewards.getCid(), confOneRewards.getNum(), addedItemList);
            Map<Integer, Integer> playCountMap = UserUtils.getPlayCountMap(userInfo);
            int placeId = confPlaceInfoManager.isExists(table.getMode(), table.getPlace());
            if (placeId > 0) {
                int playCount = playCountMap.get(placeId);
                if (playCount > 0 && playCount % 10 == 0) {
                    ItemUtils.addItem(itemInfoList, confTenRewards.getCid(), confTenRewards.getNum(), addedItemList);
                }
            }
            ItemUtils.processVirtualCurrency(addedItemList, userInfo);

            GameJiesuan.OneInfo.Builder oneInfoBuilder = GameJiesuan.OneInfo.newBuilder();
            oneInfoBuilder.setBeanChange((int) golds[seat]);
            oneInfoBuilder.setBeanTotals(userInfo.getGold());
            oneInfoBuilder.setBei(beis[seat]);
            oneInfoBuilder.setDiScore(confPlaceInfo.getBaseGold());
            oneInfoBuilder.setNickname(userInfo.getNickname());
            oneInfoBuilder.setSeat(seat);
            oneInfoBuilder.setSerialWinCount(userInfo.getSerialWin());
            oneInfoBuilder.setAddition(jiaBeis[seat]);
            oneInfoBuilder.setSpring(springs[seat]);
            if (seat == dizhuSeatIndex) {
                oneInfoBuilder.setDizhu(true);
            } else {
                oneInfoBuilder.setDizhu(false);
            }
            oneInfoBuilder.setOneRewards(confOneRewards.getNum());
            oneInfoBuilder.setTenRewards(confTenRewards.getNum());
            oneInfoBuilder.setTenRemain(UserUtils.calcTenRemain(userInfo, table.getMode(), table.getPlace(), confPlaceInfoManager));
            ItemUtils.buildProtoItem(addedItemList, oneInfoBuilder::addItem);
            jieSuanBuilder.addInfo(oneInfoBuilder.build());
        }
        Set<Map.Entry<Integer, Game.Cards>> otherCardsSet = otherCardsMap.entrySet();
        for (Map.Entry<Integer, Game.Cards> entry : otherCardsSet) {
            Integer seatIdx = entry.getKey();
            Game.Cards cards = entry.getValue();
            jieSuanBuilder.putPlayerCards(seatIdx, cards);
        }

        if (logger.isInfoEnabled()) {
            //logger.info("结算结果： \n" + jieSuanBuilder.build().toString());
        }

        //Set<Long> realPlayerIds = new HashSet<>(members.length);
        int opcode = Integer.parseInt(OpCode.cmd_jiesuan);
        for (int seat = 0; seat < members.length; seat++) {
            Player player = members[seat];
            this.sendData(player.getUid(), Crypto.isCrypto, opcode, jieSuanBuilder.build().toByteArray());
        }

        //设置了玩家准备状态，所以得更新房间数据
        TableDao tableDao = this.retrieveProxy(TableDao.class);
        tableDao.insertOrUpdateDesk(table);

        userInfoMap.forEach((Long uid, UserInfo userInfo) -> {
            if (userInfo != null) {
                boolean xuyao = isCanGetBenefit(userInfo, false);
                //if(xuyao) provideBenefit(userInfo, false);
                userDao.insertOrUpdateUser(userInfo);
            }
        });

        this.sendMessage(new Message(OpCode.update_play_task, taskMap));

        //清除本房间相关的定时调度任务
        QuartzFacade quartzFacade = (QuartzFacade) this.getFacade();
        quartzFacade.cancelQuartzSchedule(OpCode.timer_auto_play, "desk:" + table.getId());
    }

}
